#include "Analyser.h"

namespace SemanticModel
{
	using ObjectModel::ClassType;

	Analyser::Analyser(const Model& model) : _model(model)
	{
		analyse();
	}

	bool Analyser::analyse()
	{
		createClasses();
		createAttributes();
		createOperations(true);
		createOperations(false);
		makeGeneralization();
		makeImplementation();

		return false;
	}

	void Analyser::createAttributes()
	{
		for(Model::RelationsAll r = _model.relations(ContainsRelation); r.first != r.second; ++r.first)
		{
			Model::RelationAll& relation = r.first;
			ClassType* whole = _result.lookup<ClassType>(relation.roleA());
			ClassType* part = _result.lookup<ClassType>(relation.roleB());
			if (!whole || !part)
				continue;
			whole->addAttribute((L"_" + part->getName()).data(), *part);
			//auto methodName = r.first->roleB();
			//type->addMethod(methodName, );

			//*_result.lookup(L"void")
		}
	}

	void Analyser::createOperations(bool isAbstract)
	{
		SemanticModel::RelationType relationType = !isAbstract ? 
			SemanticModel::CanRelation : SemanticModel::SpecifyRelation;
		for(Model::RelationsAll r = _model.relations(relationType); r.first != r.second; ++r.first)
		{
			Model::RelationAll& relation = r.first;
			ClassType* type = _result.lookup<ClassType>(relation.roleA());
			const wchar_t* methodName = relation.roleB();
			ObjectModel::Method& method = type->addMethod(methodName,
				*_result.lookup(L"void"), isAbstract);

			
			for(Model::RelationsOut p = _model.relations(
				relation.roleB(), SemanticModel::UsesRelation);
				p.first != p.second; ++p.first)
			{
				Model::RelationOut& essesnce = p.first;
				ClassType* type = _result.lookup<ClassType>(essesnce.roleB());
				method.addParameter((type->getName() + L"_").data(), *type);
			}
		}
	}

	void Analyser::createClasses()
	{
		for (Model::Nodes n = _model.nodes(EssenseNode); n.first != n.second; ++n.first)
		{
			_result.addClass(n.first.name().data());
		}
	}

	void Analyser::makeGeneralization()
	{
		for(Model::RelationsAll r = _model.relations(IsRelation); r.first != r.second; ++r.first)
		{
			Model::RelationAll& relation = r.first;
			ClassType* generalized = _result.lookup<ClassType>(relation.roleA());
			ClassType* basic = _result.lookup<ClassType>(relation.roleB());
			generalized->setBasicClass(basic);
		}
	}
	
	void Analyser::recursiveFindAll(const wchar_t* node, 
			SemanticModel::RelationType relation, std::set<std::wstring>& result)
	{
		for(Model::RelationsOut p = _model.relations(node, relation);
			p.first != p.second; ++p.first)
		{
			Model::RelationOut& essesnce = p.first;
			const wchar_t* roleB = essesnce.roleB();
			if (result.find(roleB) == result.end())
			{
				result.insert(roleB);
				recursiveFindAll(roleB, relation, result);
			}
		}
	}

	void Analyser::makeImplementation()
	{
		for(Model::RelationsAll r = _model.relations(ImplementationRelation); 
			r.first != r.second; ++r.first)
		{
			Model::RelationAll& relation = r.first;
			std::set<std::wstring> interfaces;
			recursiveFindAll(relation.roleA(), ImplementationRelation, interfaces);
			
			ClassType* implementation = _result.lookup<ClassType>(relation.roleA());
			ClassType* baseClass = _result.lookup<ClassType>(relation.roleB());
			implementation->setBasicClass(baseClass);

			for (std::set<std::wstring>::const_iterator i = 
				interfaces.begin(); interfaces.end() != i; ++i)
			{
				ClassType* interfaceClass = _result.lookup<ClassType>(i->data());

				const ClassType::Methods& methods = interfaceClass->getMethods();
				for (ClassType::Methods::const_iterator i = 
					methods.begin(); i != methods.end(); ++i)
				{
					if (!i->isPure())
						continue;
					std::wstringstream desc;
					desc << interfaceClass->getName() << L"::" << i->getName() << L"() redefinition.";
					implementation->addMethod(i->getName().data(), i->getReturnType(), 
						false, i->getAccess(), desc.str().data());
				}
			}

		}
	}
}
